#!/bin/bash
# vim:ts=4:sw=4:noexpandtab
#
# Parallels Desktop services launcher.
#
# Copyright (c) 2004-2014 Parallels IP Holdings GmbH.
# All rights reserved.
# http://www.parallels.com


export PATH=/bin:/sbin:/usr/bin:/usr/sbin

BUNDLE_DIR="$(cd "`dirname "${0}"`/../.."; pwd)"
BOOTSTRAP="${BUNDLE_DIR}/Contents/MacOS/"
PARALLELS_SERVICE="${BOOTSTRAP}/Parallels Service"
PID_FILE=
RT_ROOT=

LIGHTWEIGHT_MODE_FLAG="${BUNDLE_DIR}/Contents/Resources/.lightweight"
AUTHORITY="Authority=Developer ID Application: Parallels, Inc."

ACTION=
MODE_LAUNCHD=0
MODE_AGENTS_ONLY=0

SLEEP_TIME=1 # in sec
MAX_WAIT_TIME=180 # in sec
CURRENT_WAITING=0

RC_OK=0
RC_FAILED=1
RC_BROKEN_SIGNATURE=2
RC="${RC_OK}"


make_rt_dirs() {
	local tmp_dir='/tmp/.pd'
	RT_ROOT="${tmp_dir}/`id -u`"
	mkdir -p "${RT_ROOT}"/{tmp,var/{run,log,tmp}} || echo "mkdir -p failed"
	chmod a+rwxt "${tmp_dir}" 2>/dev/null
}

init_runtime() {
	local uid=$(id -u)
	[ ${uid} -ne 0 ] && make_rt_dirs
	PID_FILE="${RT_ROOT}/tmp/prl_launcher.pid"
}

is_sandbox() {
	[ -f "${LIGHTWEIGHT_MODE_FLAG}" ]
}

log() {
	logger -t "launcher" -s "$@"
}

check_util() {
	if [ ! -x "${1}" ]; then
		log "'${1}' is missed or not an executable"
		return 1
	fi
	return 0
}

check_file_sign() {
	local file="${1}"
	local cs=/usr/bin/codesign
	local gr=/usr/bin/grep

	check_util "${cs}" || return 1
	check_util "${gr}" || return 1

	"${cs}" --verify "${file}" &&
		"${cs}" --display --verbose=2 --requirements - "${file}" 2>&1 |
		"${gr}" -q "${AUTHORITY}"
}

start() {
	RC="${RC_OK}"
	local start_mode="${1}"

	"${BOOTSTRAP}"/agent start &
	agent_pid=$!

	service_rc=0
	if [ $MODE_AGENTS_ONLY -eq 1 ]; then
		log "agents only"
	else
		if ! is_sandbox ; then
			check_file_sign "${PARALLELS_SERVICE}"
			if [ $? -ne 0 ]; then
				log "Wrong signature of Parallels Service"
				service_rc="${RC_BROKEN_SIGNATURE}"
			else
				"${PARALLELS_SERVICE}" service_start
				service_rc=$?
			fi
		else
			"${BOOTSTRAP}"/service start_sba
			service_rc=$?
		fi
	fi

	wait "${agent_pid}"
	agent_rc=$?

	if [ "${service_rc}" -eq 3 ]; then # broken signature
		RC="${RC_BROKEN_SIGNATURE}"
	elif [ "${agent_rc}" -ne 0 -o "${service_rc}" -ne 0 ]; then
		RC="${RC_FAILED}"
	fi

	log "Start finished. Agents: ${agent_rc}; Service: ${service_rc}"
	return "${RC}"
}

stop() {
	RC="${RC_OK}"
	local stop_mode="${1}"

	"${BOOTSTRAP}"/agent stop &
	agent_pid=$!

	service_rc=0
	launchd_rc=0

	if [ $MODE_AGENTS_ONLY -eq 0 ]; then
		if ! is_sandbox ; then
			if [ $MODE_LAUNCHD -eq 0 ]; then
				"${PARALLELS_SERVICE}" service_stop
				service_rc=$?
			fi

			"${PARALLELS_SERVICE}" check_launchd
			launchd_rc=$?
		else
			"${BOOTSTRAP}"/service stop
			service_rc=$?
		fi
	fi

	wait "${agent_pid}"
	agent_rc=$?

	if [ "${service_rc}" -eq 3 ]; then
		# broken signature
		RC="${RC_BROKEN_SIGNATURE}"
	elif [ "${agent_rc}" -ne 0 -o "${service_rc}" -ne 0 ]; then
		RC="${RC_FAILED}"
	fi

	log "Stop finished. Agents: ${agent_rc}; Service: ${service_rc};" \
		"LaunchdSetup: ${launchd_rc}"
	return "${RC}"
}

wait_instance() {
	while [ -e "$PID_FILE" ]; do
		pid=$(head -n 1 "${PID_FILE}")
		kill -0 "${pid}" >/dev/null 2>&1 || break

		if [ "${CURRENT_WAITING}" -gt "${MAX_WAIT_TIME}" ]; then
			break
		fi
		log "Waiting for another launcher instance with PID:${pid}" \
			"(${CURRENT_WAITING}/${MAX_WAIT_TIME})"
		sleep "${SLEEP_TIME}"
		((CURRENT_WAITING=$CURRENT_WAITING+$SLEEP_TIME))
	done
}


atexit() {
	rm -f "${PID_FILE}"
}

parse_cmd_line() {
	local actions='start|stop|restart'
	local options='[--agents-only] [--launchd-mode]'
	local usage="Usage: launcher ${actions} ${options}"

	case "${1}" in
		start)
			ACTION=start
			;;
		stop)
			ACTION=stop
			;;
		restart)
			ACTION=restart
			;;
		*)
			echo $usage
			exit 1
	esac
	shift
	while true; do
		case "${1}" in
			--agents-only)
				MODE_AGENTS_ONLY=1
				shift
				;;
			--launchd-mode)
				MODE_LAUNCHD=1
				shift
				;;
			*)
				[ -z "${1}" ] && break
				echo $usage
				exit 1
		esac
	done
}


init_runtime
wait_instance

trap atexit EXIT
echo $$ >"${PID_FILE}"

parse_cmd_line $@
case $ACTION in
	start)
		start
		;;
	stop)
		stop
		;;
	restart)
		stop
		start
		;;
	*)
		echo "Unsupported action ${ACTION}" >&2
		exit 1
esac

exit "${RC}"
